package com.taotao.job.rpc.core.proxy;

import com.google.common.base.Preconditions;
import com.taotao.job.rpc.core.invoker.Invoker;
import com.taotao.job.rpc.core.invoker.RemoteInvokerFactory;
import com.taotao.job.rpc.core.utils.StringUtils;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;

import java.util.concurrent.ConcurrentHashMap;

/**
 * @author fanbeibei
 * @date 2020/10/31 0:25
 */
public abstract class AbstractRpcProxyFactory implements RpcProxyFactory {
    @EqualsAndHashCode
    @AllArgsConstructor
    private static class IfaceInstanceKey {
        Class<?> ifaceClass;
        Invoker invoker;
    }

    private  final ConcurrentHashMap<IfaceInstanceKey, Object> proxyInstanceCache = new ConcurrentHashMap<>();

    private  <T> T getProxyInstance(Class<T> ifaceClass, Invoker invoker) {
        return (T) proxyInstanceCache.get(new IfaceInstanceKey(ifaceClass, invoker));
    }

    private  <T> void putProxyInstance(Class<T> ifaceClass, Invoker invoker, T instance) {
        proxyInstanceCache.putIfAbsent(new IfaceInstanceKey(ifaceClass, invoker), instance);
    }

    private RemoteInvokerFactory remoteInvokerFactory = new RemoteInvokerFactory();

    public <T> T newProxyInstance(Class<T> ifaceClass, String address){
        Preconditions.checkArgument(null != ifaceClass);
        Preconditions.checkArgument(StringUtils.isNotBlank(address));

        return newProxyInstance(ifaceClass,remoteInvokerFactory.newAddressedRemoteInvoker(address));
    }


    @Override
    public <T> T newProxyInstance(Class<T> ifaceClass, Invoker invoker) {
        Preconditions.checkArgument(null != ifaceClass);
        Preconditions.checkArgument(null != invoker);
        if(!ifaceClass.isInterface()){
            throw new IllegalArgumentException("ifaceClass is not  interface!");
        }

        T instance = (T) getProxyInstance(ifaceClass, invoker);
        if (null == instance) {
            synchronized (this){
                if (null == instance) {
                    instance = newProxyInstance0(ifaceClass, invoker);
                    putProxyInstance(ifaceClass, invoker, instance);
                }
            }
        }

        return instance;
    }

    public abstract <T> T newProxyInstance0(Class<T> ifaceClass, Invoker invoker);
}
